package com.onlyoffice.integration.services;

import com.amazonaws.ClientConfiguration;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.*;
import com.amazonaws.util.IOUtils;
import com.onlyoffice.integration.config.S3Properties;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import java.io.*;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @Classname S3FileService
 * @Description S3FileService
 * @Date 2023/8/7 11:28
 * @Author seven002
 */
@ConditionalOnBean(S3Properties.class)
@Component
@Log4j2
public class S3FileService {

    @Autowired
    private S3Properties s3Properties;

    private AmazonS3 amazonS3;

    @PostConstruct
    public void init() {
        log.info(s3Properties);
        amazonS3 = AmazonS3ClientBuilder.standard()
                .withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(s3Properties.getAccessKey(), s3Properties.getSecretKey())))
                .withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(
                        s3Properties.getEndpoint(),
                        s3Properties.getRegion()))
                .withPathStyleAccessEnabled(s3Properties.getPathStyleAccess())
                .withChunkedEncodingDisabled(true)
                .withClientConfiguration(new ClientConfiguration()
                        .withMaxConnections(s3Properties.getMaxConnections())
                        .withMaxErrorRetry(1))
                .build();
    }

    /**
     * 替换 win 文件分隔符
     *
     * @author: seven002
     * @date: 2023/8/12 18:55
     * @param: [java.lang.String]
     * @return: java.lang.String
     **/
    private String replaceWinFileSeparator(String objectName) {
        if (objectName.contains("\\")) {
            return objectName = objectName.replaceAll("\\\\", "/");
        }
        return objectName;
    }


    /**
     * 复制文件
     *
     * @param bucketName
     * @param srcObjectName
     * @param tarObjectName
     */
    public void copyObject(String bucketName, String srcObjectName, String tarObjectName) {
        srcObjectName = replaceWinFileSeparator(srcObjectName);
        tarObjectName = replaceWinFileSeparator(tarObjectName);
        amazonS3.copyObject(bucketName, srcObjectName, bucketName, tarObjectName);
    }


    /**
     * 上传文件
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @param stream     文件流
     * @throws Exception
     */
    @SneakyThrows
    public void putObject(String bucketName, String objectName, InputStream stream) {
        objectName = replaceWinFileSeparator(objectName);
        ObjectMetadata objectMetadata = new ObjectMetadata();
        objectMetadata.setContentType("application/octet-stream");
        putObject(bucketName, objectName, stream, objectMetadata);
    }

    /**
     * 上传文件
     *
     * @param bucketName     bucket名称
     * @param objectName     文件名称
     * @param stream         文件流
     * @param objectMetadata 对象元数据
     * @see <a href="http://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutObject">AWS
     * API Documentation</a>
     */

    @SneakyThrows
    private PutObjectResult putObject(String bucketName, String objectName, InputStream stream,
                                      ObjectMetadata objectMetadata) {
        objectName = replaceWinFileSeparator(objectName);
        byte[] bytes = IOUtils.toByteArray(stream);
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
        objectMetadata.setContentLength(byteArrayInputStream.available());
        // 上传
        return amazonS3.putObject(bucketName, objectName, byteArrayInputStream, objectMetadata);
    }


    /**
     * 判断object是否存在
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @see <a href="http://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetObject">AWS
     * API Documentation</a>
     */
    @SneakyThrows
    public boolean checkObjectExist(String bucketName, String objectName) {
        objectName = replaceWinFileSeparator(objectName);
        return amazonS3.doesObjectExist(bucketName, objectName);
    }

    /**
     * 获取对象元数据
     * @author: seven002
     * @date: 2023/8/12 20:29
     * @param: [java.lang.String, java.lang.String]
     * @return: com.amazonaws.services.s3.model.ObjectMetadata
     **/
    public ObjectMetadata getObjectMate(String bucketName, String objectName) {
        objectName = replaceWinFileSeparator(objectName);
        return amazonS3.getObjectMetadata(bucketName, objectName);
    }


    /**
     * 获取文件
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @return 二进制流
     * @see <a href="http://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetObject">AWS
     * API Documentation</a>
     */

    @SneakyThrows
    public S3Object getObject(String bucketName, String objectName) {
        objectName = replaceWinFileSeparator(objectName);
        return amazonS3.getObject(bucketName, objectName);
    }

    /**
     * 删除文件
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @throws Exception
     * @see <a href="http://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/DeleteObject">AWS
     * API Documentation</a>
     */

    @SneakyThrows
    public void deleteObject(String bucketName, String objectName) {
        objectName = replaceWinFileSeparator(objectName);
        amazonS3.deleteObject(bucketName, objectName);
    }

    /**
     * 根据文件前置查询文件集合
     *
     * @param bucketName bucket名称
     * @param prefix     前缀
     * @param recursive  是否递归查询
     * @return S3ObjectSummary 列表
     * @see <a href="http://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/ListObjects">AWS
     * API Documentation</a>
     */

    @SneakyThrows
    public List<S3ObjectSummary> getAllObjectsByPrefix(String bucketName, String prefix,
                                                       boolean recursive) {
        prefix=replaceWinFileSeparator(prefix);
        ObjectListing objectListing = amazonS3.listObjects(bucketName, prefix);
        return new ArrayList<>(objectListing.getObjectSummaries());
    }


    /**
     * 开放链接，默认public没有设置访问权限
     * url 规则：${endPoint}/${bucketName}/${objectName}
     *
     * @param bucketName
     * @param objectName
     * @return
     */
    public String generatePublicUrl(String bucketName, String objectName) {
        objectName = replaceWinFileSeparator(objectName);
        return s3Properties.getEndpoint() + "/" + bucketName + "/" + objectName;
    }

}
